#include <utility>
#include <sdl.hpp>

//----------------------//
// C style
struct sItem {
  int flags;
  int d;
};
typedef sItem *handle_t;

handle_t init(int flags) { auto p = new sItem; p->flags = flags; p->d = 0; return p; }
bool set_func(handle_t handle, int d) { if (!handle) return false; handle->d = d; return true; }
bool get_func(handle_t handle, int *d) { if (!handle) return false; *d = handle->d; return true; }
bool get_flags(handle_t handle, int *f) { if (!handle) return false; *f = handle->flags; return true; }
void uninit(handle_t handle) { if (handle) delete handle; }

//----------------------//
// wrapper to c++
using sdl::Wrapper;
using sdl::ClassWrapped;

class Handle : public ClassWrapped<Handle, sItem> {
  public:
    static Wrapper<Handle, sItem> init(int flags) {
      auto h = ::init(flags);
      return Wrapper<Handle, sItem>(h);
    }
    bool set_func(int d) { return ::set_func(get(), d); }
    bool get_func(int *d) { return ::get_func(get(), d); }
    bool get_flags(int *f) { return ::get_flags(get(), f); }
    void uninit() { ::uninit(get()); }
};

//----------------------//
#include <iostream>

void log(const char *msg1 = nullptr, const char *msg2 = nullptr) {
  if (msg1) std::cout << msg1;
  if (msg2) std::cout << msg2;
  std::cout << std::endl;
}

template <typename T, typename C>
void dump(Wrapper<T, C> &h) {
  int d = 0, f = 0;
  h->get_func(&d);
  h->get_flags(&f);
  std::cout << "flags: " << f << ", data: " << d << std::endl;
}

int main(int argc, char *argv[]) {
  auto h1 = Handle::init(1);
  auto h2 = Handle::init(2);
  auto h3 = Handle::init(3);
  if (h1 && h2 && h3) {
    h1->set_func(10);
    h2->set_func(20);
    h3->set_func(30);
    auto c1 = h1.get();
    auto c2 = h2.get();
    auto c3 = (sItem*)h3;
    h3 = h1;
    log("----- begin info -----");
    dump(h1);
    dump(h2);
    dump(h3);
    log("----- all h2 -----");
    h1 = h2;
    dump(h1);
    dump(h2);
    dump(h3);
    log("----- recover to begin info -----");
    h1 = c1;
    h2.reset();
    dump(h1);
    dump(h2);
    dump(h3);
    log("----- equal info -----");
    log("FALSE -> h1 is equal to h2: ", h1 == h2 ? "true" : "false");
    log("TRUE  -> h1 is equal to h3: ", h1 == h3 ? "true" : "false");
    log("TRUE  -> h1 is not equal to h2: ", h1 != h2 ? "true" : "false");
    log("FALSE -> h1 is not equal to h3: ", h1 != h3 ? "true" : "false");
    log("TRUE  -> h3 is equal to c1: ", h3 == c1 ? "true" : "false");
    log("FALSE -> h3 is not equal to c1: ", h3 != c1 ? "true" : "false");
    log("FALSE -> h1 is equal to nullptr: ", h1 == nullptr ? "true" : "false");
    log("FALSE -> nullptr is equal to h1: ", nullptr == h1 ? "true" : "false");
    log("TRUE  -> h2 is equal to nullptr: ", h2 == nullptr ? "true" : "false");
    log("TRUE  -> nullptr is equal to h2: ", nullptr == h2 ? "true" : "false");
    log("TRUE  -> h1 is not equal to nullptr: ", h1 != nullptr ? "true" : "false");
    log("TRUE  -> nullptr is not equal to h1: ", nullptr != h1 ? "true" : "false");
    log("FALSE -> h2 is not equal to nullptr: ", h2 != nullptr ? "true" : "false");
    log("FALSE -> nullptr is not equal to h2: ", nullptr != h2 ? "true" : "false");
    log("----- reset info -----");
    h3.reset(c3);
    h1.reset(nullptr);
    dump(h1);
    dump(h2);
    dump(h3);
    log("----- swap info -----");
    h3.swap(h1);
    dump(h1);
    dump(h2);
    dump(h3);

    log("----- reset and release -----");
    h1.reset(c1);
    h2.reset(c2);
    h3.reset(c3);
    dump(h1);
    dump(h2);
    dump(h3);
    h1->uninit();
    h2->uninit();
    h3->uninit();
    log("----- end info -----");
  }

  return 0;
}